package com.ronz.sparsearray;

import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description 01_稀疏数组：
 *              以五子棋棋盘为例，可以将五子棋棋盘看为一个二维数组。未落子的以0表示，落白子以1表示，黑子以2表示。
 *              可以肯定的是，棋盘中肯定有很多重复的元素，因此可以用一个稀疏数组来表示。
 *
 *              需求：
 *              - 将五子棋棋盘的当前落子情况（即二维数组）转换成一个稀疏数组，并将稀疏数组存到本地文件。
 *              - 从本地文件中读取稀疏数组，并可以将稀疏数组还原为二维数组
 *
 *              笔记所在路径：F:\Notes\Data Structures and Algorithms\尚硅谷_韩顺平_笔记&源码
 * @Author Ronz
 * @Date 2020/12/15 18:33
 * @Version 1.0
 */
public class No1_SparseArray {
    public static void main(String[] args) {

        /**
         * 一、创建原始二维数组（棋盘）
         */
        // 1. 创建一个 11x11 的二维数组(也即棋盘)
        int[][] array = new int[11][11];

        // 2. 假设白子是 1，黑子是 2
        // 我们先随机赋值两个，假设是下了两个棋子
        array[1][2] = 1;
        array[2][3] = 2;

        // 3. 打印这个二维数组
        System.out.println("原始数组为：");
        for (int[] arr : array){
            for (int item : arr){
                System.out.print(item + "\t");
            }
            System.out.println();
        }

        /**
         * 二、创建稀疏数组
         */
        // 因为实际题目中不知道具体有多少个有效元素，所以必须遍历二维数组
        // 1. 遍历二维数组，获取有效元素个数
        int count = 0;                  // 记录有效元素个数
        for (int[] arr: array){
            for (int item : arr){
                if (item != 0){
                    count++;
                }
            }
        }

        // 2. 根据有效元素个数来创建稀疏数组
        int[][] sparseArray = new int[count+1][3];      // 稀疏数组是一个 nx3 的二维数组

        // 3. 给稀疏数组第 0 行赋值，赋值的内容是原始数组的信息：行数、列数、有效元素个数
        sparseArray[0][0] = 11;
        sparseArray[0][1] = 11;
        sparseArray[0][2] = count;

        // 4. 遍历原始数组。当遇见有效元素时，将行、列、元素值存储到稀疏数组中。
        int x = 1;                  // 稀疏数组行索引
        for (int i=0;i<11;i++){
            for (int j=0;j<11;j++){
                if (array[i][j] != 0){          // 判断是不是有效元素，如果是，则记录下来信息
                    sparseArray[x][0] = i;
                    sparseArray[x][1] = j;
                    sparseArray[x++][2] = array[i][j];
                }
            }
        }

        // 5. 打印稀疏数组
        System.out.println("稀疏数组为：");
        for(int[] arr : sparseArray){
            for (int item : arr){
                System.out.print(item + "\t");
            }
            System.out.println();
        }

        // 假设要有重新载入功能，因此要做到可以将稀疏数组写入到文件中
        writeFile(sparseArray);

        /**
         * 三、将稀疏数组恢复为原始二维数组
         */
        // 假设要有重新载入功能，所以首先要从文件中读出
        readFile(sparseArray);


        // 1. 首先读取稀疏数组的第 0 行，第 0 行包括原始二维数组的行列数
        int row = sparseArray[0][0];        // 获取行数
        int col = sparseArray[0][1];        // 获取列数

        // 2. 创建原始数组
        int[][] originArr = new int[row][col];

        // 3. 遍历稀疏数组中记录的有效元素,准备填充到原始数组
        for (int i=1; i<=sparseArray[0][2]; i++){       // 稀疏数组的第0行、第3列记录了有效元素的个数
            originArr[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];    // 填充到原始数组
        }

        // 4. 打印恢复出来的原始数组
        System.out.println("通过稀疏数组恢复的原始数组为：");
        for (int[] arr : originArr){
            for (int item : arr){
                System.out.print(item + "\t");
            }
            System.out.println();
        }

    }

    /**
     * @Description 将稀疏数组写入到文件中
     * @Param []
     * @return void
     */
    public static void writeFile(int[][] sparseArray){
        File file = new File("sparseArray.txt");
        if (!file.exists()){
            try {
                boolean newFile = file.createNewFile();
                if (newFile){
                    System.out.println(file.getName() + " 创建成功！");
                }else{
                    System.out.println(file.getName() + " 创建失败!");
                }

                BufferedWriter writer = new BufferedWriter(new FileWriter(file));
                // 将二维的稀疏数组中的内容写入到文件中
                for (int[] arr : sparseArray){
                    for (int item : arr){
                        writer.write(item + " ");
                    }
                    writer.newLine();
                }
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @Description 从文件中读取数据到稀疏数组中
     * @Param [array]
     * @return void
     */
    public static void readFile(int[][] sparseArray){
        try {
            BufferedReader reader = new BufferedReader(new FileReader("sparseArray.txt"));

            // 下面使用了集合的流式编程
            Stream<String> lines = reader.lines();  // 流中为 {"11 11 2", "1 2 1", "2 3 2"}
            List<String> list = lines.map(line -> line.split(" "))  // 将流中的每个字符串映射为字符串数组
                    .flatMap(Arrays::stream)        // 将每个字符串数组再归并为流
                    .collect(Collectors.toList());  // 保存到 list 集合中

            sparseArray[0][0] = Integer.parseInt(list.get(0));      // 首先给稀疏数组的第一行赋值
            sparseArray[0][1] = Integer.parseInt(list.get(1));
            sparseArray[0][2] = Integer.parseInt(list.get(2));
            for (int i = 3; i < list.size() - 3; i++){
                sparseArray[i/3][i%3] = Integer.parseInt(list.get(i));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
